package org.apache.ibatis.executor.parameter;

import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.apache.ibatis.session.Configuration;

import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List;

// 默认的参数处理器
public class DefaultParameterHandler implements ParameterHandler {

  private final TypeHandlerRegistry typeHandlerRegistry;

  private final MappedStatement mappedStatement;

  private final Object parameterObject; // 参数本身对象

  private BoundSql boundSql;

  private Configuration configuration;


  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  public Object getParameterObject() {
    return parameterObject;
  }



  // 为 PreparedStatement 设置参数
  public void setParameters(PreparedStatement ps) throws SQLException {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());

    // 参数映射
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

    if (parameterMappings != null) {

        // 元标签参数对象
        MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);


      for (int i = 0; i < parameterMappings.size(); i++) {

          // 取出一个
        ParameterMapping parameterMapping = parameterMappings.get(i);

        if (parameterMapping.getMode() != ParameterMode.OUT) {  // 不是OUT

          Object value; // 要给 PreparedStatement 中 '?'  赋值的参数值

          // 属性名称
          String propertyName = parameterMapping.getProperty();

          // Tokenizer n. 分词器；编译器
          PropertyTokenizer prop = new PropertyTokenizer(propertyName);


          if (parameterObject == null) { // 元标签参数对象 为空

            value = null;

          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { // 包含有该 typeHandler

            value = parameterObject; // 就是本身

          } else if (boundSql.hasAdditionalParameter(propertyName)) {
            value = boundSql.getAdditionalParameter(propertyName);

          } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) && boundSql.hasAdditionalParameter(prop.getName())) {
            value = boundSql.getAdditionalParameter(prop.getName());
            if (value != null) {
              value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
            }
          } else {
            value = metaObject == null ? null : metaObject.getValue(propertyName);
          }

          // 得到 TypeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();

          if (typeHandler == null) {
            throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement " + mappedStatement.getId());
          }

          // 设置参数
          typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
        }
      }
    }
  }

}
